Initialization

Strategies discussion

Strategies (n)
  • RAII.

  • Smart Pointers.

  • new/delete malloc/free.

  • try/catch.

  • Rust borrow checker.

Strategies (n+1) (Grouped element thinking and systems)
  • The entity has a block allocator, where all memory used by the entity uses that block allocator.

    • Sounds nice, but doesn't seem to solve the problem of memory being used in other systems.

  • Every frame has an arena allocator. Everything that should exist for a single frame uses that allocator.

    • Casey and Jon use this.

  • Use PEAK memory to know what size the block should have.

    • Very interesting.

Strategies (n+2)
  • ZII and Stubs as alternative ways of memory structure and error handling.

  • Casey about Stubs:

    • Stubs are just a block of zeroes.

    • "If you failed to push something into the arena, you just get a Stub back, it's all zeroed".

    • "When something comes back, just produces a Stub instead of something real, and everything still works".

    • The code doesn't check if the struct is a Stub. The code just assumes the data is something you can use and uses it.

    • For example, when using a 0 Handle, I don't return null, I just return a zeroed entity.

  • Stubs :

    • Stubs are minimal or fake implementations of functions, classes or modules, used mainly for testing, incremental development or simulating external dependencies.

    #include <iostream>
    
    // Real function not yet implemented, we use a temporary stub
    int calcularImposto(float salario) {
        std::cout << "[STUB] Function not yet implemented." << std::endl;
        return 0; // Returns a fixed value to continue development
    }
    
    int main() {
        float salario = 5000;
        int imposto = calcularImposto(salario);
        std::cout << "Calculated tax: " << imposto << std::endl;
        return 0;
    }
    

ZII (Zero Initialization Is Initialization)

Comparing to RAII
  • Unlike RAII, ZII does not inherently imply resource ownership or cleanup in the destructor.

  • It's about safe default values , not lifecycle management.

  • Both are part of the broader design goal of:

    • Avoiding uninitialized or unsafe state.

    • Ensuring predictable object behavior starting at construction.

  • Because of this shared initialization focus , they're often discussed together in contexts like:

    • C++ safety idioms.

    • Codebases transitioning from C-style to C++.

    • Embedded/safety-critical environments.

  • Their conceptual scopes differ, but they both leverage constructors  to achieve safety and predictability, hence the mental association.

About
  • ZII is true by default for every system.

  • The way you program is by letting zero always be an acceptable value.

  • ZII is a principle that defends that variables be initialized to zero or default values as soon as they are created. This reduces the risk of accessing undefined values.

  • Main characteristics :

    • Prevents undefined behavior when accessing uninitialized variables.

    • Applicable mainly to primitive types and arrays.

    • Avoids hard-to-debug errors.

  • Advantages of ZII :

    • Reduces errors related to uninitialized values.

    • Ensures predictability in program behavior.

    • Useful for data structures and dynamically allocated memory.

Examples
#include <iostream>

class Example {
public:
    int x = 0;  // Zero initialization
    double y = 0.0;
};

int main() {
    Example e;
    std::cout << "x: " << e.x << ", y: " << e.y << std::endl;
    return 0;
}
int arr[10] = {};  // All elements are initialized to 0

RAII (Resource Acquisition Is Initialization)

About and Meaning
  • Bjarne Stroustrup - "RAII is the best thing the language has" .

    • "Constructors and Destructors pairs is the best feature implemented in the language".

    • "Sometimes this comes out in the name of RAII".

      • "Not the greatest name I've ever heard".

  • The phrase “Resource Acquisition Is Initialization”  emphasizes that:

    • Acquiring a resource happens at the same time the object is initialized (constructed).

    • This ties the resource's lifetime to the object’s lifetime.

  • Why Destruction Is Implied :

    • Although destruction is not named, it's implied by C++'s object lifetime rules :

      • If the resource is acquired during initialization, and the object controls the resource.

      • Then releasing it must naturally occur when the object is destroyed.

    • C++ deterministic destruction ensures that destructors are called at the end of scope, enabling automatic cleanup.

  • Therefore, RAII relies on both construction and destruction, even if the name only mentions the construction side.

Principles
  • Associates resource acquisition (like memory, file handles, mutexes, etc.) with the construction  of an object, and resource release with its destruction .

  • Scope-Based Lifetime :

    • In C++, objects declared with automatic storage duration (i.e., local stack variables) are automatically destroyed when they go out of scope.

  • Destructor Role :

    • The destructor is the mechanism used to release resources. Since C++ guarantees that destructors of local objects are called when the scope exits (either normally or via exception), this ensures deterministic cleanup.

  • Why it "implies" a destructor :

    • For RAII to work, a resource-managing object must reliably release its resource.

    • C++ destructors are guaranteed to be called when the object’s scope ends.

    • Therefore, RAII relies on this guarantee, and the destructor becomes the point where the resource is released.

Examples
#include <iostream>
#include <fstream>

class FileHandler {
private:
    std::ofstream file;
public:
    FileHandler(const std::string& filename) {
        file.open(filename);
        if (!file.is_open()) {
            throw std::runtime_error("Failed to open the file.");
        }
    }
    ~FileHandler() {
        file.close();
    }
    void write(const std::string& text) {
        file << text << std::endl;
    }
};

int main() {
    try {
        FileHandler fh("test.txt");
        fh.write("RAII ensures the file is closed.");
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
    // The FileHandler destructor closes the file automatically.
    return 0;
}
Negative points
  • RAII in C++ and comparison with Odin .

    • Odin is soooo much better, wow.

  • Makes you allocate and deallocate simple things individually.

  • Sometimes you want to be explicit about things and just free a bunch of things at once.

    • In C++ that would require you to not use the language as it was intended in some ways.

    • In C++ you have to accept the complexity of RAII.

  • Casey Muratori - RAII .

    • Automatic destructors make you think about failure and error handling in a bad way.

    • Unwinding the object through a destructor is not going to make the error go away. The error needs to be handled depending on the circumstance.

    • You need to think about failures and program around it.

    • In RAII you spend a lot of time writing code to handle failures that will never happen, but the same errors blow up.

    • RAII doesn't solve the problem they needed to solve.

    • Some people criticized his quote, for not making a distinction between RAII and error handling.